package org.cxn.tools.simple;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by chenxiangning on 2015/8/10.
 */
public class FileTool {
    protected static final Logger logger = LogManager.getLogger(FileTool.class);

    // 限制实例化
    private FileTool() {
    }

    /**
     * 从文本文件中读取所有的行。
     *
     * @param path 文件全路径。
     * @return 返回以集合列表形式存储的所有行。
     * @throws java.io.IOException 读取文件有误时抛出。
     */
    public static List<String> readLines(String path) throws IOException {

        List<String> lines = new ArrayList<String>();// 用于存放读出的字符列表。
        String line;// 读取到的每行数据。
        BufferedReader br = new BufferedReader(new FileReader(path));// 输入流。
        while ((line = br.readLine()) != null) {// 循环读取文件。
            lines.add(line);
        }
        close(br);

        return lines;
    }

    /**
     * 将字符串数组按行写入文件，如果文件已经存在将被覆盖。
     *
     * @param path  写入的文件路径。
     * @param lines 需要写入的数据集合。
     * @throws java.io.IOException 写入文件有误时抛出。
     */
    public static void writeLines(String path, List<String> lines)
            throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(path));
        for (String line : lines) {
            bw.write(line);
            bw.newLine();
        }
        close(bw);
    }

    /**
     * 打开文件并得到BufferedReader。
     *
     * @param file 要打开的文件的全路径。
     * @return BufferedReader 如果文件不存在，返回null
     * @throws java.io.IOException 读取文件有误时抛出
     */
    public static BufferedReader getReader(File file) throws IOException {
        if (isFile(file)) {
            return new BufferedReader(new FileReader(file));
        }
        return null;
    }

    /**
     * 打开文件，如果文件不存在，自动创建，并得到BufferedWriter。
     *
     * @param file           文件
     * @param allowOverwrite 是否运行覆盖旧文件
     * @return BufferedWriter，如果文件存在但不运行覆盖，返回null
     * @throws java.io.IOException 读取或创建新文件有误时抛出
     */
    public static BufferedWriter getWriter(File file, boolean allowOverwrite)
            throws IOException {
        if (checkFile(file, allowOverwrite)) {
            return new BufferedWriter(new FileWriter(file));
        }
        return null;
    }

    /**
     * 打开文件并得到InputStrea
     *
     * @param file 要打开的文件。
     * @return FileInputStream，如果文件不存在，返回null
     * @throws java.io.IOException 读取文件有误时抛出
     */
    public static FileInputStream getInputStream(File file) throws IOException {
        if (isFile(file)) {
            return new FileInputStream(file);
        }
        return null;
    }

    /**
     * 打开文件，如果文件不存在，自动创建，并得到FileOutputStream。
     *
     * @param file           文件
     * @param allowOverwrite 是否运行覆盖旧文件
     * @return FileOutputStream，如果文件存在但不允许覆盖，返回null
     * @throws java.io.IOException 读取或创建新文件有误时抛出
     */
    public static FileOutputStream getOutputStream(File file,
                                                   boolean allowOverwrite) throws IOException {
        if (checkFile(file, allowOverwrite)) {
            return new FileOutputStream(file, allowOverwrite);
        }
        return null;
    }

    /**
     * 关闭字节输入流。
     *
     * @param inputStream 要关闭的字节输入流。
     * @return 返回关闭的结果。如果字节输入流为空或字节输入流关闭成功返回true；否则返回false
     */
    public static boolean close(InputStream inputStream) {
        if (inputStream != null) {
            try {
                inputStream.close();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * 关闭字节输出流。
     *
     * @param outputStream 要关闭的字节输出流。
     * @return 返回关闭的结果。如果字节输出流为空或字节输出流关闭成功返回true；否则返回false
     */
    public static boolean close(OutputStream outputStream) {
        if (outputStream != null) {
            try {
                outputStream.close();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * 关闭Reader输入。
     *
     * @param reader 要关闭的Reader输入流。
     * @return 返回关闭的结果。如果Reader输入流为空或Reader输入流关闭成功返回true；否则返回false
     */
    public static boolean close(Reader reader) {
        if (reader != null) {
            try {
                reader.close();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * 关闭Writer输出流。
     *
     * @param writer 要关闭的Writer输出流。
     * @return 返回关闭的结果。如果Writer输出流为空或Writer输出流关闭成功返回true；否则返回false
     */
    public static boolean close(Writer writer) {
        if (writer != null) {
            try {
                writer.close();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /**
     * 移动文件。
     *
     * @param file     要移动的文件。
     * @param destPath 目的文件夹。
     * @throws IOException 移动文件出错抛出该异常。
     */
    public static void moveFile(File file, String destPath) throws IOException {
        if (isFile(file) && (destPath != null)) {
            File destDir = new File(destPath);
            if (!destDir.exists()) {
                destDir.mkdir();
            }

            FileInputStream in = new FileInputStream(file);
            FileOutputStream out = new FileOutputStream(destDir.getPath()
                    + File.separator + file.getName());

            // 传输字节
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            close(in);
            close(out);
            file.delete();
        }
    }

    /**
     * 复制文件。
     *
     * @param file     要复制的文件。
     * @param destFile 目的文件。
     * @throws IOException 移动文件出错抛出该异常。
     */
    public static void copyFile(File file, File destFile) throws IOException {
        if (isFile(file) && (destFile != null)) {
            if (!destFile.exists()) {
                throw new IOException("file" + destFile.getPath()
                        + "yi cun zai\"" + destFile.getPath() + "");
            }

            checkFile(destFile, false);

            FileInputStream in = new FileInputStream(file);
            FileOutputStream out = new FileOutputStream(destFile);

            // 传输字节
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            close(in);
            close(out);
        }
    }

    /**
     * 批量重命名文件夹下的文件，包括文件夹。
     *
     * @param dir     目录
     * @param prefix  重命名的后文件的前缀
     * @param find    查找
     * @param replace 替换为
     * @param suffix  重命名后文件的后缀
     * @return 被重命名过的文件
     */
    public static File[] batchRename(File dir, String prefix, String find,
                                     String replace, String suffix) {
        List<File> ls = new ArrayList<File>();
        if (isDirectory(dir)) {
            File[] files = dir.listFiles();
            for (File file : files) {
                String oldName = file.getName();
                String newName = prefix + oldName.replaceAll(find, replace)
                        + suffix;
                if (!newName.equals(oldName)) {
                    File dest = new File(file.getParent() + File.separator
                            + newName);
                    if (file.renameTo(dest)) {
                        ls.add(dest);
                    }
                }
            }
        }
        return ls.toArray(new File[ls.size()]);
    }

    /**
     * 批量删除一个目录下的所有文件（夹），该目录并不删除。
     *
     * @param dir 要删除子对象的目录。
     */
    public static void deleteSubs(File dir) {
        if (isDirectory(dir)) {
            File[] files = dir.listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteSubs(file);
                }
                file.delete();
            }
        }
    }

    /**
     * 删除一个目录。该目录下的所有对象同时全部删除。
     *
     * @param file 要删除的目录。
     */
    public static void deleteDirectory(File file) {
        if (isDirectory(file)) {
            deleteSubs(file);
            file.delete();
        }
    }

    /**
     * 检查文件是否存在，如果不存在自动创建。
     *
     * @param file           要打开的文件。
     * @param allowOverwrite 是否允许
     * @return 返回结果。如果文件存在，且不允许覆盖，返回false。
     * @throws IOException 创建文件出错时抛出此异常。
     */
    private static boolean checkFile(File file, boolean allowOverwrite)
            throws IOException {
        if (file != null) {
            // 检查父目录，不存在时自动创建
            File parent = file.getParentFile();
            boolean parentExist = true;
            if ((parent != null) && !parent.exists()) {
                parentExist = parent.mkdirs();
            }
            if (parentExist
                    && (file.createNewFile() || (file.exists() && allowOverwrite))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断一个File对象否是文件。
     *
     * @param file 要判断的File对象。
     * @return 返回判断的结果，如果文件不为空并且文件存在并且该对象是文件则返回true；否则返回false。
     */
    private static boolean isFile(File file) {
        return (file != null) && file.exists() && file.isFile();
    }

    /**
     * 判断一个File对象是否是目录。
     *
     * @param dir 要判断的File对象。
     * @return 返回判断的结果。如果File不为空并且文件存在并且该对象是目录，则返回true；否则返回false。
     */
    private static boolean isDirectory(File dir) {
        return (dir != null) && dir.exists() && dir.isDirectory();
    }
}
